package com.sxpi.user.service.useramount;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sxpi.common.result.R;
import com.sxpi.user.dto.UserAmountDto;
import com.sxpi.user.mapper.UserAmountRecordMapper;
import com.sxpi.user.mapper.UserMapper;
import com.sxpi.user.pojo.User;
import com.sxpi.user.pojo.UserAmountRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;

/**
 * @Author: 董伟豪
 * @Date: 2022/11/8 21:14
 */
@Service
@Slf4j
public class IUserAmountRecordServiceImpl extends ServiceImpl<UserAmountRecordMapper, UserAmountRecord>
        implements IUserAmountRecordService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 提现方法---无锁
     * @param userAmountDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // @GlobalTransactional
    public synchronized R takeMoney(UserAmountDto userAmountDto) {
        // 提现的用户
        Long userId = userAmountDto.getUserId();
        // 提现的金额
        Double money = userAmountDto.getMoney();
        // 无锁
        User user = userMapper.selectById(userId);
        // 判断用户提现的余额是否充足，如果充足就提现
        if(user != null && user.getAmount().doubleValue() - money > 0){
            // 如果余额充足，就开始扣减余额
            User updateUser = new User();
            updateUser.setId(userId);
            updateUser.setAmount(new BigDecimal(user.getAmount().doubleValue() - money));
            // 这行代码只是为了同步一些余额，避免再次查询数据库
            user.setAmount(updateUser.getAmount());
            // 更新用户余额
            userMapper.updateById(updateUser);

            // 同时插入提现记录信息保存到用户提现表中
            UserAmountRecord userAmountRecord = new UserAmountRecord();
            userAmountRecord.setUserId(userId);
            userAmountRecord.setUsername(user.getUsername());
            userAmountRecord.setAvatar(user.getAvatar());
            userAmountRecord.setMoney(new BigDecimal(money));
            // 保存用户提现记录
            this.saveOrUpdate(userAmountRecord);

            // 输出日志
            log.info("当前提现的金额是：{}，用户的余额是：{}", money, user.getAmount());
        }else{
            // 否则就返回账户余额不足
            return R.error("账户不存在，或者余额不足!!!");
        }
        return R.ok();
    }

    /**
     * 提现方法---悲观锁
     * 悲观锁是一种数据库级别的锁，必须要数据库支持，也就是在执行某个业务的时候们每次并发的线程在获取数据的时候们仍未其他的线程会对数据进行修改，
     * 故而每次在获取数据时候都会上锁。==而其他的线程访问该数据的时候就会发生阻塞的想象。==只有最终地当前线程释放了共享自愿的额锁，
     * 其他的线程才会获得锁，并共享资源进行操作。
     * @param userAmountDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // @GlobalTransactional
    public R takeMoneyPessimismLock(UserAmountDto userAmountDto) {
        // 提现的用户
        Long userId = userAmountDto.getUserId();
        // 提现的金额
        Double money = userAmountDto.getMoney();
        // 悲观锁
        User user = userMapper.selectUserPessimismLock(userId);
        // 判断用户提现的余额是否充足，如果充足就提现
        if(user != null && user.getAmount().doubleValue() - money > 0){
            // 如果余额充足，就开始扣减余额
            User updateUser = new User();
            updateUser.setId(userId);
            updateUser.setAmount(new BigDecimal(user.getAmount().doubleValue() - money));
            // 这行代码只是为了同步一些余额，避免再次查询数据库
            user.setAmount(updateUser.getAmount());
            // 更新用户余额
            userMapper.updateById(updateUser);

            // 同时插入提现记录信息保存到用户提现表中
            UserAmountRecord userAmountRecord = new UserAmountRecord();
            userAmountRecord.setUserId(userId);
            userAmountRecord.setUsername(user.getUsername());
            userAmountRecord.setAvatar(user.getAvatar());
            userAmountRecord.setMoney(new BigDecimal(money));
            // 保存用户提现记录
            this.saveOrUpdate(userAmountRecord);

            // 输出日志
            log.info("当前提现的金额是：{}，用户的余额是：{}", money, user.getAmount());
        }else{
            // 否则就返回账户余额不足
            return R.error("账户不存在，或者余额不足!!!");
        }
        return R.ok();
    }

    /**
     * 提现方法---乐观锁
     * 由于mysql底层执行数据记录的时候update/insert/delete的时候，采用的是串行执行的方式，
     * 在version进行控制的时候只有一个线程才会执行成功
     * @param userAmountDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // @GlobalTransactional
    public R takeMoneyOptimismLock(UserAmountDto userAmountDto) {
        // 提现的用户
        Long userId = userAmountDto.getUserId();
        // 提现的金额
        Double money = userAmountDto.getMoney();
        // 悲观锁
        User user = userMapper.selectById(userId);
        // 判断用户提现的余额是否充足，如果充足就提现
        if(user != null && user.getAmount().doubleValue() - money > 0){
            // 使用乐观锁 javaA ---- MYSQL--A ----version = 0 --updateUserAmount = version = 1
            // 使用乐观锁 javaB ---- MYSQL--B ----version = 0 --updateUserAmount
            // 使用乐观锁 javaC ---- MYSQL--C ----version = 0 --updateUserAmount
            int res = userMapper.updateUserAmount(userId,money,user.getVersion());
            if(res > 0){
                // 同时插入提现记录信息保存到用户提现表中
                UserAmountRecord userAmountRecord = new UserAmountRecord();
                userAmountRecord.setUserId(userId);
                userAmountRecord.setUsername(user.getUsername());
                userAmountRecord.setAvatar(user.getAvatar());
                userAmountRecord.setMoney(new BigDecimal(money));
                // 保存用户提现记录
                this.saveOrUpdate(userAmountRecord);

                // 输出日志
                log.info("当前提现的金额是：{}，用户的余额是：{}", money, user.getAmount());
            }

            // 如果余额充足，就开始扣减余额
            User updateUser = new User();
            updateUser.setId(userId);
            updateUser.setAmount(new BigDecimal(user.getAmount().doubleValue() - money));
            // 这行代码只是为了同步一些余额，避免再次查询数据库
            user.setAmount(updateUser.getAmount());
            // 更新用户余额
            userMapper.updateById(updateUser);

        }else{
            // 否则就返回账户余额不足
            return R.error("账户不存在，或者余额不足!!!");
        }
        return R.ok();
    }
}
